#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>

// 错误码定义
#define ERROR_PCM_OPEN_FAILED -1
#define ERROR_HW_PARAMS_FAILED -2
#define ERROR_PCM_READ_FAILED -3

// 打开并配置PCM设备
int open_pcm_device(snd_pcm_t **playback, const char *device, snd_pcm_format_t format, unsigned int rate, int channels, snd_pcm_uframes_t frames) {
    int err;
    snd_pcm_hw_params_t *hw_params;

    // 打开PCM设备
    if ((err = snd_pcm_open(playback, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        fprintf(stderr, "无法打开PCM设备：%s\n", snd_strerror(err));
        return ERROR_PCM_OPEN_FAILED;
    }

    // 分配硬件参数结构体
    snd_pcm_hw_params_alloca(&hw_params);

    // 初始化硬件参数
    if ((err = snd_pcm_hw_params_any(*playback, hw_params)) < 0) {
        fprintf(stderr, "无法初始化硬件参数：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    // 设置访问类型
    if ((err = snd_pcm_hw_params_set_access(*playback, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
        fprintf(stderr, "无法设置访问类型：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    // 设置采样格式
    if ((err = snd_pcm_hw_params_set_format(*playback, hw_params, format)) < 0) {
        fprintf(stderr, "无法设置采样格式：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    // 设置通道数
    if ((err = snd_pcm_hw_params_set_channels(*playback, hw_params, channels)) < 0) {
        fprintf(stderr, "无法设置通道数：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    // 设置采样率
    if ((err = snd_pcm_hw_params_set_rate_near(*playback, hw_params, &rate, NULL)) < 0) {
        fprintf(stderr, "无法设置采样率：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    // 设置每个周期的帧数
    if ((err = snd_pcm_hw_params_set_period_size_near(*playback, hw_params, &frames, NULL)) < 0) {
        fprintf(stderr, "无法设置周期帧数：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    // 应用硬件参数
    if ((err = snd_pcm_hw_params(*playback, hw_params)) < 0) {
        fprintf(stderr, "无法应用硬件参数：%s\n", snd_strerror(err));
        snd_pcm_close(*playback);
        return ERROR_HW_PARAMS_FAILED;
    }

    return 0; // 成功返回0
}

// 从文件读取音频数据并播放
int play_audio_from_file(snd_pcm_t *playback, const char *filename, snd_pcm_uframes_t frames, int channels) {
    int err;
    FILE *file;
    char *buffer;

    // 打开输入文件
    if ((file = fopen(filename, "rb")) == NULL) {
        fprintf(stderr, "无法打开输入文件\n");
        snd_pcm_close(playback);
        return ERROR_PCM_READ_FAILED;
    }

    // 分配缓冲区
    buffer = (char *)malloc(snd_pcm_frames_to_bytes(playback, frames));
    if (!buffer) {
        fprintf(stderr, "无法分配缓冲区内存\n");
        fclose(file);
        snd_pcm_close(playback);
        return ERROR_PCM_READ_FAILED;
    }

    // 读取并播放数据
    while (1) {
        size_t read_size = fread(buffer, 1, snd_pcm_frames_to_bytes(playback, frames), file);
        if (read_size == 0) {
            break; // 文件读取完毕
        }

        err = snd_pcm_writei(playback, buffer, frames);
        if (err < 0) {
            if (err == -EPIPE) {
                // 缓冲区溢出
                snd_pcm_prepare(playback);
            } else {
                fprintf(stderr, "播放音频失败：%s\n", snd_strerror(err));
                free(buffer);
                fclose(file);
                snd_pcm_close(playback);
                return ERROR_PCM_READ_FAILED;
            }
        }
    }

    // 释放资源
    free(buffer);
    fclose(file);
    snd_pcm_close(playback);

    return 0; // 成功返回0
}

int main(int argc, char *argv[]) {
    const char *pcm_device = "hw:0,0";
    snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
    unsigned int rate = 44100;
    int channels = 2;
    snd_pcm_uframes_t frames = 4096;
    const char *input_file = "output.pcm";
    snd_pcm_t *playback;
    int ret;

    // 打开并配置PCM设备
    ret = open_pcm_device(&playback, pcm_device, format, rate, channels, frames);
    if (ret != 0) {
        printf("音频设备配置失败，错误码：%d\n", ret);
        return ret;
    }

    // 从文件读取音频并播放
    ret = play_audio_from_file(playback, input_file, frames, channels);
    if (ret == 0) {
        printf("音频播放成功\n");
    } else {
        printf("音频播放失败，错误码：%d\n", ret);
    }

    return ret;
}
